package com.ycy.servlet;

import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;


import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.http.HttpResponseException;
import com.ycy.ss.Constant;
import com.ycy.ss.status.AllStatus;
import com.ycy.util.GoogleUtil;

public class GoogleAuth extends HttpServlet{
	private static final Logger log = Logger.getLogger(GoogleAuth.class);

	/** Lock on the flow and credential. */
	private final Lock lock = new ReentrantLock();

	/** Persisted credential associated with the current request or {@code null} for none. */
	private Credential credential;

	/**
	 * Authorization code flow to be used across all HTTP servlet requests or {@code null} before initialized in
	 * {@link #initializeFlow()}.
	 */
	private AuthorizationCodeFlow flow;

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException{
		if ("1".equals(req.getParameter("clear"))) {
			AllStatus.google = null;
			resp.getWriter().write("clear");
			return;
		}
		
		lock.lock();
		try {
			// load credential from persistence store
			String userId = getUserId(req);
			if (flow == null) {
				flow = initializeFlow();
			}
			credential = flow.loadCredential(userId);
			// if credential found with an access token, invoke the user code
			if (credential != null && credential.getAccessToken() != null) {
				try {
					super.service(req, resp);
					return;
				} catch (HttpResponseException e) {
					// if access token is null, assume it is because auth failed and we need to re-authorize
					// but if access token is not null, it is some other problem
					if (credential.getAccessToken() != null) {
						throw e;
					}
				}
			}
			// redirect to the authorization flow
			String redirectUri = getRedirectUri(req);
			resp.sendRedirect(flow.newAuthorizationUrl().setRedirectUri(redirectUri).build());
			credential = null;
		} finally {
			lock.unlock();
		}
	}

	/**
	 * If the user already has a valid credential held in the AuthorizationCodeFlow they are simply returned to the home
	 * page.
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		response.sendRedirect("/status.jsp");
	}

	/**
	 * Returns the URI to redirect to with the authentication result.
	 */
	protected String getRedirectUri(HttpServletRequest request) throws ServletException, IOException{
		return Constant.getAccountInfo("google.oauth_redirect_uri");
	}

	/**
	 * Returns the HTTP session id as the identifier for the current user. The users credentials are stored against this
	 * ID.
	 */
	protected String getUserId(HttpServletRequest request) throws ServletException, IOException{
		return request.getSession(true).getId();
	}

	protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException{
		return GoogleUtil.getFlow();
	}

	/**
	 * Return the persisted credential associated with the current request or {@code null} for none.
	 */
	protected final Credential getCredential(){
		return credential;
	}
}
